package jnpf.engine.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jnpf.engine.custom.constants.GlobalProcessVariableConstants;
import jnpf.engine.entity.ActHiActInstEntity;
import jnpf.engine.enums.FlowTaskStatusEnum;
import jnpf.engine.mapper.ActHiActInstMapper;
import jnpf.exception.WorkFlowException;
import jnpf.util.RedisUtil;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.RepositoryServiceImpl;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

import static jnpf.engine.custom.constants.BasicActivitiConstants.THIS_STEP_NAME;
import static jnpf.engine.custom.constants.RedisConstants.LOCK_STR;

/**
 * @author LoveMyOrange
 * @create 2022-01-06 14:13
 */
@Component
public class ActivitiUtils {

    @Resource
    private HistoryService historyService;
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private ActHiActInstMapper actHiActInstMapper;
    @Resource
    private RedisUtil redisUtil;

    public void validProcessStatus(String processInstanceId){
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        boolean suspended = processInstance.isSuspended();
        if(suspended){
            throw new WorkFlowException("该流程已经被挂起!,请先恢复!");
        }
        boolean exists = redisUtil.exists(LOCK_STR + processInstanceId);
        if(exists){
            throw new WorkFlowException("该流程已经被锁定!,请先解锁!");
        }
    }

    public String getCurrentNodeNameStr(String processInstanceId){
        List<HistoricActivityInstance> list = historyService.createNativeHistoricActivityInstanceQuery().sql("SELECT ID_ , ACT_NAME_  FROM  ACT_HI_ACTINST WHERE PROC_INST_ID_ = #{processInstanceId} " +
                "AND END_TIME_ IS NULL").parameter("processInstanceId",processInstanceId).list();
        List<String> names=new ArrayList<>();
        if(CollUtil.isNotEmpty(list)){
            for (HistoricActivityInstance historicActivityInstance : list) {
                names.add(historicActivityInstance.getActivityName());
            }
            names=CollUtil.distinct(names);
        }
//        LambdaQueryWrapper<ActHiActInstEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//        lambdaQueryWrapper.eq(ActHiActInstEntity::getProcessInstanceId,processInstanceId);
//        lambdaQueryWrapper.isNull(ActHiActInstEntity::getEndTime);
//        List<ActHiActInstEntity> actHiActInstEntities = actHiActInstMapper.selectList(lambdaQueryWrapper);
        return CollUtil.join(names,",");
    }

    public List<String> getCurrentNodeIdOnlyUserTaskList(String processInstanceId){

        List<HistoricActivityInstance> list = historyService.createNativeHistoricActivityInstanceQuery().sql("SELECT ID_ , ACT_NAME_ ,ACT_ID_ FROM  ACT_HI_ACTINST WHERE PROC_INST_ID_ = #{processInstanceId} " +
                "AND END_TIME_ IS NULL AND  ACT_TYPE_ = #{bb}").parameter("processInstanceId",processInstanceId).parameter("bb","userTask").list();
        List<String> names=new ArrayList<>();
        if(CollUtil.isNotEmpty(list)){
            for (HistoricActivityInstance historicActivityInstance : list) {
                names.add(historicActivityInstance.getActivityId());
            }
            names = CollUtil.distinct(names);
        }
        return names;
    }


    public  List<String> getActivityIds(String processInstanceId){
        List<String> activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId);
        if(CollUtil.isEmpty(activeActivityIds)){
            return CollUtil.newArrayList(/*"流程已结束"*/);
        }
        else{
            return CollUtil.newArrayList(activeActivityIds);
        }
    }

    public  String getCurrentNodeName(Process process,List<String> ids){
        ArrayList<String> distinct = CollUtil.distinct(ids);
        if(distinct.size()==1){
            FlowElement flowElement = process.getFlowElement(distinct.get(0),true);
            return flowElement.getName();
        }
        else if(distinct.size()>1){
            List<String>  names= new ArrayList<>();
            for (String id : distinct) {
                FlowElement flowElement = process.getFlowElement(id,true);
                names.add(flowElement.getName());
            }
            return StrUtil.join(",",names);
        }
        return THIS_STEP_NAME;
    }

    public List<String> getNextNode(String processDefinitionId,String currentActivity){
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        FlowElement flowElement = bpmnModel.getMainProcess().getFlowElement(currentActivity);
        List<String> nodeNextList = new ArrayList<>();
        getNextNode(nodeNextList,flowElement);
        return nodeNextList;
    }

    private void getNextNode(List<String> nodeNextList, FlowElement flowElement){
        if(flowElement instanceof Task){
            Task task=(Task) flowElement;
            nodeNextList.add(task.getId());
        }
        else if(flowElement instanceof Gateway){
            Gateway gateway =(Gateway)flowElement;
            List<SequenceFlow> outgoingFlows = gateway.getOutgoingFlows();
            for (SequenceFlow outgoingFlow : outgoingFlows) {
                FlowElement targetFlowElement = outgoingFlow.getTargetFlowElement();
                getNextNode(nodeNextList,targetFlowElement);
            }
        }
    }
    /**
     * 通过流程定义id 获取 流程定义实体对象
     * @param processDefinitionId
     * @return
     */
    public ProcessDefinitionEntity getProcessDefinitionEntityById(String processDefinitionId){
        return (ProcessDefinitionEntity) ((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(processDefinitionId);
    }

    /**
     * 判断流程状态
     * @param processInstanceId
     * @return
     */
    public Boolean checkStatus(String processInstanceId){
        HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).variableName(GlobalProcessVariableConstants.PROCESS_STATUS).singleResult();
        Integer status = (Integer) historicVariableInstance.getValue();
        if(ObjectUtil.equal(status, FlowTaskStatusEnum.Draft.getCode())
          || ObjectUtil.equal(status, FlowTaskStatusEnum.Reject.getCode())
          || ObjectUtil.equal(status, FlowTaskStatusEnum.Revoke.getCode())
        ){
              return true;
        }
        else{
            return false;
        }

    }

    /**
     * 通过流程实例id或者 业务主键 或者 任务id 获取流程实例
     * @param id
     * @return
     */
    public ProcessInstance getProcessInstanceByIdOrBusinessKey(String id){
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
        if(processInstance==null){
            processInstance = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(id).singleResult();
            if(processInstance==null){
                HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult();
                return runtimeService.createProcessInstanceQuery().processInstanceId(historicTaskInstance.getProcessInstanceId()).singleResult();
            }
        }
        return processInstance;
    }

    public HistoricProcessInstance getHistoricProcessInstanceByIdOrBusinessKeyOrTaskId(String id){
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult();
        if(historicProcessInstance==null){
            historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(id).includeProcessVariables().singleResult();
            if(historicProcessInstance==null){
                HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult();
                return historyService.createHistoricProcessInstanceQuery().processInstanceId(historicTaskInstance.getProcessInstanceId()).includeProcessVariables().singleResult();
            }
        }
        return historicProcessInstance;
    }
}
